package com.yan.qing.bootaspect.aop;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import com.yan.qing.bootaspect.Annotitaion.OperateLogs;
import com.yan.qing.bootaspect.operate.OperateBusinessEnum;
import com.yan.qing.bootaspect.operate.OperateTypeEnum;
import com.yan.qing.bootaspect.operate.OperateUserTypeEnum;
import com.yan.qing.bootaspect.request.OperateLogsSaveRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 操作日志记录切面
 *
 **/
@Slf4j
@Aspect
@Component
public class OperationLogRecordAspect {


    @Pointcut(value = "@annotation(com.yan.qing.bootaspect.Annotitaion.OperateLogs)")
    public void operateLogsAnnotation() {
    }

    @Around(value = "operateLogsAnnotation()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            ServletRequestAttributes requestAttributes =
                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            assert requestAttributes != null;
            HttpServletRequest request = requestAttributes.getRequest();

            // 获取请求参数
            List<Object> params = new ArrayList<>();
            for (Object arg : joinPoint.getArgs()) {
                if (arg == null) {
                    continue;
                } else if (arg instanceof ServletRequest) {
                    continue;
                } else if (arg instanceof ServletResponse) {
                    continue;
                }
                params.add(arg);
            }

            // ip
            String ip = ServletUtil.getClientIP(request);
            // token
            String token = request.getHeader("Authorization");

            // 获取注解数据
            OperateLogs operateLogs = ((MethodSignature) joinPoint.getSignature())
                    .getMethod()
                    .getAnnotation(OperateLogs.class);
            // 操作业务
            OperateBusinessEnum operateBusiness = operateLogs.operateBusiness();
            // 操作类型
            OperateTypeEnum operationType = operateLogs.operateType();
            // 操作用户类型
            OperateUserTypeEnum operateUserType = operateLogs.operateUserType();
            // 操作对象标题
            String operateTitle = operateLogs.operateTitle();
            // 操作详情
            String operationDetail = operateLogs.operateDetail();
            operationDetail = replaceStrParams(params, operationDetail, token);
            // 操作对象id
            String targetId = operateLogs.targetId();
            targetId = replaceStrParams(params, targetId, token);

            // 设置保存传输对象
            OperateLogsSaveRequest operateLogsSaveRequest = new OperateLogsSaveRequest();
            operateLogsSaveRequest.setUserId(99L);
            operateLogsSaveRequest.setUsername("ccc");
            operateLogsSaveRequest.setRealName("sss");
            operateLogsSaveRequest.setPhone("43247923");
            operateLogsSaveRequest.setToken(token);
            operateLogsSaveRequest.setParams(JSON.toJSONString(params));
            operateLogsSaveRequest.setTargetId(targetId);
            operateLogsSaveRequest.setOperateBusiness(operateBusiness);
            operateLogsSaveRequest.setOperationType(operationType);
            operateLogsSaveRequest.setOperateUserType(operateUserType);
            operateLogsSaveRequest.setOperateTitle(operateTitle);
            operateLogsSaveRequest.setOperateDetail(operationDetail);
            operateLogsSaveRequest.setIp(ip);
            Date now = new Date();
            operateLogsSaveRequest.setCreateTime(now);
            operateLogsSaveRequest.setCreateDate(Integer.valueOf(DateUtil.format(now, "yyyyMMdd")));


            // 发送消息记录日志mq
            System.out.println(JSON.toJSONString(operateLogsSaveRequest));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        return joinPoint.proceed();
    }

    /**
     * 从请求参数中获取指定数据
     *
     * @param objects 请求参数
     * @param key     数据key
     * @return 获取数据
     **/
    private String getParams(List<Object> objects, String key) {
        for (Object object : objects) {
            String jsonString = JSONUtil.toJsonStr(object);

            JSONValidator validator = JSONValidator.from(jsonString);

            if (validator.validate() && validator.getType() == JSONValidator.Type.Object) {
                JSONObject jsonObject = JSONObject.parseObject(jsonString);
                if (jsonObject.containsKey(key)) {
                    return jsonObject.getString(key);
                }
            }
        }
        return "";
    }


    /**
     * 替换字符串中 ${} 占位符
     *
     * @param params     请求参数
     * @param replaceStr 待替换字符串
     * @param token      token
     * @return 替换后字符串
     **/
    private String replaceStrParams(List<Object> params, String replaceStr, String token) {
        String REG = "(?<=(?<!\\\\)\\$\\{)(.*?)(?=(?<!\\\\)\\})";
        ArrayList<String> list = ReUtil.findAll(REG, replaceStr, 0, new ArrayList<>());
        if (CollectionUtil.isEmpty(list)) {
            return replaceStr;
        }

        for (String key : list) {
            if ("date".equals(key)) {
                replaceStr = StrUtil.replace(
                        replaceStr,
                        "${" + key + "}",
                        DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
                );
            } else if ("tokenUserName".equalsIgnoreCase(key)) {
                replaceStr = StrUtil.replace(
                        replaceStr,
                        "${" + key + "}",""
                );
            } else if ("tokenRealName".equalsIgnoreCase(key)) {
                replaceStr = StrUtil.replace(
                        replaceStr,
                        "${" + key + "}",
                        ""
                );
            } else if (StrUtil.isNotBlank(key)) {
                String value = getParams(params, key);
                replaceStr = StrUtil.replace(replaceStr, "${" + key + "}", value);
            }
        }

        return replaceStr;
    }

}
